#ifndef PRYN_GUI_PAINTER_H
#define PRYN_GUI_PAINTER_H

typedef struct PrynGuiPainter PrynGuiPainter;
typedef struct PrynGuiBrush PrynGuiBrush;
typedef enum PrynGuiBrushType PrynGuiBrushType;
typedef struct PrynGuiPen PrynGuiPen;
typedef enum PrynGuiPenType PrynGuiPenType;
typedef enum PrynGuiPenRepeat PrynGuiPenRepeat;
typedef union PrynGuiBrushTypeValue PrynGuiBrushTypeValue;

/// Text alignment value.
typedef enum PrynGuiTextAlign PrynGuiTextAlign;

#include <pryn/result.h>
#include <pryn/state.h>
#include <pryn/utility.h>

/** @defgroup PrynGuiPainter PrynGuiPainter C API
	@{ */

PrynGuiImport (PrynResult, prynGuiPainterCreateForHwnd, (PrynGuiPainter **output, PrynState *state, void *window, void *device, void *rectangle)) ///< Create a Microsoft Windows painter. This returns PrynResultFailure if this cannot happen.
PrynGuiImport (PrynResult, prynGuiPainterDestroy, (PrynGuiPainter *painter)) ///< Free any resources allocated by the painter. You should only call this if you created the painter.

PrynGuiImport (PrynResult, prynGuiPainterState, (PrynGuiPainter *painter, PrynState **output)) ///< State the painter is using.

PrynGuiImport (PrynResult, prynGuiPainterClear, (PrynGuiPainter *painter, PrynColor color)) ///< Clear the entire painting area to a single color.
PrynGuiImport (PrynResult, prynGuiPainterFillRectangleColor, (PrynGuiPainter *painter, int left, int top, int right, int bottom, PrynColor color)) ///< Clear an area to a single color.

PrynGuiImport (PrynResult, prynGuiPainterClip, (PrynGuiPainter *painter, int left, int top, int right, int bottom)) ///< Intersect the current clipping region (which is the entire painting area by default) with the region given. The right and bottom edge are not in the clipping region.
PrynGuiImport (PrynResult, prynGuiPainterClipReset, (PrynGuiPainter *painter)) ///< Reset the clipping region to the entire painting area.

PrynGuiImport (PrynResult, prynGuiPainterTranslate, (PrynGuiPainter *painter, int x, int y)) ///< Add a translation factor to painting. After this function call, any coordinates given will have (@e x, @e y) added to them.

/** @name Text Printing
These functions draw and measure text, and control the current font.
@{ */

	PrynGuiImport (PrynResult, prynGuiPainterPrint, (PrynGuiPainter *painter, int x, int y, const PrynString *string)) ///< Print text at the given position, whose interpretation depends upon the current text alignment.
	PrynGuiImport (PrynResult, prynGuiPainterPrintArea, (PrynGuiPainter *painter, int left, int top, int right, int bottom, const PrynString *string)) ///< Print text within the box. Left and right alignment put the text against the @e left and @e right edges; @e top and @e bottom alignment put it against the top and bottom edges. Baseline alignment is treated as @e top.

	PrynGuiImport (PrynResult, prynGuiPainterFontHeight, (PrynGuiPainter *painter, int height)) ///< Set the height of a character in pixels, changing the font.

	PrynGuiImport (PrynResult, prynGuiPainterSetTextAlign, (PrynGuiPainter *painter, PrynGuiTextAlign align)) ///< Set text alignment.
	PrynGuiImport (PrynResult, prynGuiPainterTextAlign, (PrynGuiPainter *painter, PrynGuiTextAlign *result)) ///< Retrieve the text alignment.
	PrynGuiImport (PrynResult, prynGuiPainterTextFits, (PrynGuiPainter *painter, const PrynString *string, int width, size_t *output)) ///< Output how many characters of the string fits into the given width.
	PrynGuiImport (PrynResult, prynGuiPainterWordFits, (PrynGuiPainter *painter, const PrynString *string, int width, size_t *output)) ///< Output how many characters of the string fit into the given width, rounded down to the last word. For example, if "A quick brown fox" fits up to the "o" in "brown", this will return 7 for "A quick".
	PrynGuiImport (PrynResult, prynGuiPainterMeasureText, (PrynGuiPainter *painter, const PrynString *string, int *width, int *height)) ///< Measure how much space the text takes.

	PrynGuiImport (PrynResult, prynGuiPainterCountTextLines, (PrynGuiPainter *painter, const PrynString *text, int width, int *output)) ///< Count the number of lines it would take for the current font and settings to render this text using prynGuiPainterPrintArea.
	PrynGuiImport (PrynResult, prynGuiPainterTextAdvance, (PrynGuiPainter *painter, int *value)) ///< Retrieve the number of pixels between each row of text in the current font settings.

/** @} */
/** @name Brush
These functions affect the current brush, which is used to fill the insides of drawn shapes.
@{ */
	
	PrynGuiImport (PrynResult, prynGuiPainterSetBrush, (PrynGuiPainter *painter, const PrynGuiBrush *brush)) ///< Assign the brush.
	PrynGuiImport (PrynResult, prynGuiPainterSetBrushNone, (PrynGuiPainter *painter)) ///< Remove the current brush so that nothing is drawn.
	PrynGuiImport (PrynResult, prynGuiPainterSetBrushColor, (PrynGuiPainter *painter, PrynColor color)) ///< Change to a solid color brush.

/** @} */
/** @name Pen
These functions affect the current pen, which is used to draw lines around objects.
@{ */

	PrynGuiImport (PrynResult, prynGuiPainterSetPen, (PrynGuiPainter *painter, const PrynGuiPen *pen)) ///< Assign the pen.
	PrynGuiImport (PrynResult, prynGuiPainterSetPenNone, (PrynGuiPainter *painter)) ///< Remove the current pen so that nothing is drawn.
	PrynGuiImport (PrynResult, prynGuiPainterSetPenSolid, (PrynGuiPainter *painter, int width, PrynColor color)) ///< Change to a solid color pen.
	PrynGuiImport (PrynResult, prynGuiPainterSetPenPattern, (PrynGuiPainter *painter, int width, PrynColor fallback, int patternWidth, int patternHeight, PrynColor *pattern, int offset, PrynGuiPenRepeat repeat)) ///< Change to a pattern pen. If this is unsupported, the fallback color will be used to create a solid pen instead.

/** @} */
/** @name Shapes and Lines
These functions draw shapes and lines using the current brush and pen.
@{ */

	PrynGuiImportVariadic (PrynResult, prynGuiPainterLine, (PrynGuiPainter *painter, int pointCount, ...)) ///< Draw a line connecting between a set of points. Each point is two integers for x, y coordinates. The line is drawn using the current pen.
	PrynGuiImport (PrynResult, prynGuiPainterLineData, (PrynGuiPainter *painter, int pointCount, int *points)) ///< Draw a line connecting a set of points. Each point is two integers for x, y coordinates. The line is drawn using the current pen.

	PrynGuiImport (PrynResult, prynGuiPainterRectangle, (PrynGuiPainter *painter, int left, int top, int right, int bottom)) ///< Draw a rectangle using the current brush and pen.
	PrynGuiImport (PrynResult, prynGuiPainterCircle, (PrynGuiPainter *painter, int centerX, int centerY, int radius)) ///< Draw a circle using the current brush and pen.

/** @} */

/// Text alignment, which is composed of horizontal and vertical components.
enum PrynGuiTextAlign
{
	/** @name Horizontal component
	@{ */
	PrynGuiTextAlignHorizontalShift = 0, ///< Shift to the horizontal setting.
	PrynGuiTextAlignHorizontalMask = 0xFF << PrynGuiTextAlignHorizontalShift, ///< Mask to isolate the horizontal alignment component.
	
	PrynGuiTextAlignLeft = 0, ///< Horizontal reference point is on the left edge of the bounding box.
	PrynGuiTextAlignRight = 1 << PrynGuiTextAlignHorizontalShift, ///< Horizontal reference point is on the right edge of the bounding box.
	PrynGuiTextAlignCenter = 2 << PrynGuiTextAlignHorizontalShift, ///< Horizontal reference point is in the middle of the bounding box.

	/** @}
	@name Vertical component
	@{ */
	
	PrynGuiTextAlignVerticalShift = 8, ///< Shift to the vertical setting.
	PrynGuiTextAlignVerticalMask = 0xFF << PrynGuiTextAlignVerticalShift, ///< Mask to isolate the vertical alignment component.
	
	PrynGuiTextAlignTop = 0, ///< Vertical reference point is on the top edge of the bounding box.
	PrynGuiTextAlignBottom = 1 << PrynGuiTextAlignVerticalShift, ///< Vertical reference point is on the bottom edge of the bounding box.
	PrynGuiTextAlignBaseLine = 2 << PrynGuiTextAlignVerticalShift, ///< Vertical reference point is on the baseline of the first row.

	/** @} */
};

/// Type of a brush.
enum PrynGuiBrushType
{
	PrynGuiBrushTypeNone = 0, ///< Doesn't draw anything for the brush.
	PrynGuiBrushTypeColor = 1, ///< Single solid color, value in value.color.
};

/// Union containing brush type data, used in PrynGuiBrush.
union PrynGuiBrushTypeValue
{
	PrynColor color; ///< Color for a PrynGuiBrushTypeColor brush.
};

/// A brush for filling areas.
struct PrynGuiBrush
{
	PrynGuiBrushType type; ///< Type of the brush.
	PrynGuiBrushTypeValue value; ///< Brush value.

#ifdef __cplusplus
	inline PrynGuiBrush () : type (PrynGuiBrushTypeNone) { } ///< Initialise to an empty brush.
	static inline PrynGuiBrush None () { return PrynGuiBrush (); } ///< Return an empty brush.
	static inline PrynGuiBrush Color (PrynColor value) { PrynGuiBrush brush; brush.type = PrynGuiBrushTypeColor; brush.value.color = value; return brush; } ///< Return a color brush.
#endif /* __cplusplus */
};

/// Type of a pen.
enum PrynGuiPenType
{
	PrynGuiPenTypeNone = 0, ///< Doesn't draw anything for the pen.
	PrynGuiPenTypeSolid = 1, ///< Solid line of color.
	PrynGuiPenTypePattern = 2, ///< A pattern is oriented and repeated, if supported; otherwise this acts like a solid pen.
};

/// Pen repeating pattern.
enum PrynGuiPenRepeat
{
	PrynGuiPenRepeatSolid = 0, ///< The pen should be solid.
	PrynGuiPenRepeatDash = 1, ///< The pen should be dashed, with long stretches of paint and equal skipped stretches.
};

/// A pen for drawing the outlines.
struct PrynGuiPen
{
	PrynGuiPenType type; ///< Type of the pen.
	PrynColor color; ///< Color of the pen for a PrynGuiPenTypeSolid pen, and the fallback color for PrynGuiPenTypePattern.
	int width; ///< Width of the pen in pixels, where zero means it's always one pixel wide.
	
	int imageWidth, imageHeight; ///< Dimensions of the image.
	PrynColor *image; ///< Image used for the PrynGuiPenTypePattern type in [x + y * imageWidth] order.
	int offset; ///< Offset of the image used for PrynGuiPenTypePattern type.
	PrynGuiPenRepeat repeat;

#ifdef __cplusplus
	inline PrynGuiPen () : type (PrynGuiPenTypeNone), color (0), width (0), imageWidth (0), imageHeight (0), image (0), offset (0), repeat (PrynGuiPenRepeatSolid) { } ///< Initialises to an empty pen.
	static inline PrynGuiPen Solid (PrynColor color, int width = 1, PrynGuiPenRepeat repeat = PrynGuiPenRepeatSolid) { PrynGuiPen pen; pen.type = PrynGuiPenTypeSolid; pen.color = color; pen.width = width; pen.repeat = repeat; return pen; } ///< Return a solid color pen.
	static inline PrynGuiPen Pattern (int width, int imageWidth, int imageHeight, PrynColor *image, int offset, PrynColor fallbackColor = 0, PrynGuiPenRepeat fallbackRepeat = PrynGuiPenRepeatSolid) { PrynGuiPen pen; pen.width = width; pen.color = fallbackColor; pen.width = width; pen.imageWidth = imageWidth; pen.imageHeight = imageHeight; pen.image = image; pen.offset = offset; pen.repeat = fallbackRepeat; return pen; } ///< Return a pattern pen with a solid color fallback.
#endif /* __cplusplus */
};

#ifdef PrynGuiInternalStructs
/** State for painting to a control. */
struct PrynGuiPainter
{
#ifdef PrynGuiInternal
/** @name Internal fields
	These fields are not normally visible to clients, and it is recommended not to use them to maximise binary compatibility. To make them available, define PrynEditorInternal.
	@{ */

	PrynState *pState; ///< State link.
	void *pPlatform; ///< Platform-specific information.
	int pLeft, pTop, pRight, pBottom; ///< Area being rendered to.

/** @} */
#endif /* PrynGuiInternal */

#ifdef __cplusplus
	static inline PrynGuiPainter *CreateForHwnd (PrynState *state, void *window, void *device, void *rectangle) { PrynGuiPainter *result; state->checkError (prynGuiPainterCreateForHwnd (&result, state, window, device, rectangle)); return result; } ///< Create a Microsoft Windows painter.

	inline void destroy (PrynGuiPainter *painter) { prynGuiPainterDestroy (this); } ///< Free any resources allocated by the painter. You should only call this if you created the painter.

	inline PrynResult checkError (PrynResult result) { return prynCheckErrorBase (state (), result); } ///< If the result is below zero, throw an error.

	inline PrynState *state () { PrynState *state; prynCheckErrorNull (prynGuiPainterState (this, &state)); return state; } ///< State the painter is using.

	inline void clear (PrynColor color) { checkError (prynGuiPainterClear (this, color)); } ///< Clear the entire painting area to a single color.
	inline void fillRectangleColor (int left, int top, int right, int bottom, PrynColor color) { checkError (prynGuiPainterFillRectangleColor (this, left, top, right, bottom, color)); } ///< Clear an area to a single color.

	inline void clip (int left, int top, int right, int bottom) { checkError (prynGuiPainterClip (this, left, top, right, bottom)); } ///< Intersect the current clipping region (which is the entire painting area by default) with the region given. The right and bottom edge are not in the clipping region.

	inline void clipReset () { checkError (prynGuiPainterClipReset (this)); } ///< Reset the clipping region to the entire painting area.

/** @name Text Printing
These functions draw and measure text, and control the current font.
@{ */

	inline void print (int x, int y, const PrynString &string) { checkError (prynGuiPainterPrint (this, x, y, &string)); } ///< Print text at the given position, whose interpretation depends upon the current text alignment.
	inline void print (int left, int top, int right, int bottom, const PrynString &string) { checkError (prynGuiPainterPrintArea (this, left, top, right, bottom, &string)); } ///< Print text within the box. Left and right alignment put the text against the @e left and @e right edges; @e top and @e bottom alignment put it against the top and bottom edges. Baseline alignment is treated as @e top.

	inline void fontHeight (int height) { checkError (prynGuiPainterFontHeight (this, height)); }///< Set the height of a character in pixels, changing the font.

	inline PrynGuiTextAlign textAlign () { PrynGuiTextAlign result; checkError (prynGuiPainterTextAlign (this, &result)); return result; }
	inline void textAlign (PrynGuiTextAlign align) { checkError (prynGuiPainterSetTextAlign (this, align)); } ///< Set text alignment.

	inline size_t textFits (const PrynString &string, int width) { size_t result; checkError (prynGuiPainterTextFits (this, &string, width, &result)); return result; } ///< Output how many characters of the string fits into the given width.
	inline size_t wordFits (const PrynString &string, int width) { size_t result; checkError (prynGuiPainterWordFits (this, &string, width, &result)); return result; } ///< Output how many characters of the string fit into the given width, rounded down to the last word. For example, if "A quick brown fox" fits up to the "o" in "brown", this will return 7 for "A quick".
	inline void measureText (const PrynString &string, int *width, int *height) { checkError (prynGuiPainterMeasureText (this, &string, width, height)); } ///< Measure how much space the text takes.
	inline int countTextLines (const PrynString &text, int width) { int result; checkError (prynGuiPainterCountTextLines (this, &text, width, &result)); return result; } ///< Count the number of lines it would take for the current font and settings to render this text using #print.
	inline int textAdvance () { int result; checkError (prynGuiPainterTextAdvance (this, &result)); return result; } ///< Retrieve the number of pixels between each row of text in the current font settings.

/** @} */
/** @name Brush
These functions affect the current brush, which is used to fill the insides of drawn shapes.
@{ */

	inline void brush (const PrynGuiBrush &brush) { checkError (prynGuiPainterSetBrush (this, &brush)); } ///< Assign the brush.
	inline void brushNone () { checkError (prynGuiPainterSetBrushNone (this)); } ///< Remove the current brush so that nothing is drawn.
	inline void brushColor (PrynColor color) { checkError (prynGuiPainterSetBrushColor (this, color)); } ///< Change to a solid color brush.

/** @} */
/** @name Pen
These functions affect the current pen, which is used to draw lines around objects.
@{ */

	inline void pen (const PrynGuiPen &pen) { checkError (prynGuiPainterSetPen (this, &pen)); } ///< Assign the pen.
	inline void penNone () { checkError (prynGuiPainterSetPenNone (this)); } ///< Remove the current pen so that nothing is drawn.
	inline void penSolid (int width, PrynColor color) { checkError (prynGuiPainterSetPenSolid (this, width, color)); } ///< Change to a solid color pen.
	inline void penPattern (int width, PrynColor fallback, int patternWidth, int patternHeight, PrynColor *pattern, int offset = 0, PrynGuiPenRepeat repeat = PrynGuiPenRepeatSolid) { checkError (prynGuiPainterSetPenPattern (this, width, fallback, patternWidth, patternHeight, pattern, offset, repeat)); } ///< Change to a pattern pen. If this is unsupported, the fallback color will be used to create a solid pen instead.

/** @} */
/** @name Shapes and Lines
These functions draw shapes and lines using the current brush and pen.
@{ */

	inline void line (int pointCount, ...) { va_list arguments; va_start (arguments, pointCount); checkError (prynGuiPainterLineData (this, pointCount, (int *) arguments)); va_end (arguments); } ///< Draw a line connecting between a set of points. Each point is two integers for x, y coordinates. The line is drawn using the current pen.
	inline void lineData (int pointCount, int *points) { checkError (prynGuiPainterLineData (this, pointCount, points)); } ///< Draw a line connecting a set of points. Each point is two integers for x, y coordinates. The line is drawn using the current pen.

	inline void rectangle (int left, int top, int right, int bottom) { checkError (prynGuiPainterRectangle (this, left, top, right, bottom)); } ///< Draw a rectangle using the current brush and pen.
	inline void circle (int centerX, int centerY, int radius) { checkError (prynGuiPainterCircle (this, centerX, centerY, radius)); } ///< Draw a circle using the current brush and pen.

/** @} */

#endif /* __cplusplus */
};
#endif /* PrynGuiInternalStructs */

/** @} */ // (PrynGuiPainter group end)


#endif /* PRYN_GUI_PAINTER_H */
